﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

using Matrix = Microsoft.Xna.Framework.Matrix;
using BlobTowerDefense.Tools;
using BlobTowerDefense.Managers;


namespace BlobTowerDefense.Game_Entities
{
    #region modifiers

    // modifiers for enemy
    public enum ENEMYMOD {NORMAL, FREEZE, STUN, POISON, UNKNOWN};

    public class EnemyEntity
    {
        // Modifers on enemies as freeze etc.
        private class modif
        {
            public uint time;
            public uint times;

            public modif(uint time = 0, uint times = 0)
            {
                this.times = times;
                this.time = time;
            }

            public void ClearTimes()
            {
                times = 0;
            }
        }

        private Dictionary<ENEMYMOD,modif> modifiers; // Active modifiers on enemy -- zatim neni vyuzita (Konecny)

    #endregion

        public Model Model { get; protected set; }

        private int hp;               //health points
        public int MoneyForKilling { get; private  set; }  //money for killing enemy
        private float speed;
        private int armor;
        private int MaxHp;
        public float Scale;
        public bool IsAlive { get; private set; }         //true if enemy is alive (hp > 0), otherwise false

        private Curve3D path;

        public Vector3 Position; // actual position of enemy

        float internalTime;
        private const float addTime = 0.0006f;

        SpriteBatch mBatch;
        Texture2D mHealthBar;

        private SpriteFont font1;
        private List<int> hitDamages;
        private List<float> hitStartTimes;

        public EnemyEntity(EnemyStats stats, Curve3D path)
        {
            this.hp = stats.Hp;
            this.MaxHp = stats.Hp;
            this.armor = stats.Armor;
            this.MoneyForKilling = stats.MoneyForKilling;
            this.speed = stats.Speed;
            this.IsAlive = true;
            this.path = path;
            this.Position = path.GetPosition(0.0f);
            this.internalTime = 0.0f;
            this.Scale = 1.0f;
            modifiers = new Dictionary<ENEMYMOD,modif>();
            modifiers[ENEMYMOD.FREEZE] = new modif();
            modifiers[ENEMYMOD.POISON] = new modif();

            this.Model = GamePage.content.Load<Model>(String.Format("Models/Enemies/{0}", stats.EnemyType.ToString()));

            mBatch = new SpriteBatch(GamePage.Device);
            mHealthBar = GamePage.content.Load<Texture2D>("Images/Misc/HealthBar");
            font1 = GamePage.content.Load<SpriteFont>("Fonts/Font1");

            hitDamages = new List<int>();
            hitStartTimes = new List<float>();
        }


        /// <summary>
        /// Reducing healt points
        /// </summary>
        /// <param name="rHp"></param>
        public void ReduceHP(int rHp)
        {
            hp -= rHp;
            this.hitDamages.Add(rHp);
            if (hp < 0)
                hp = 0;

            if (hp == 0)
            {
                IsAlive = false;
                GamePage.PlayerMoney += this.MoneyForKilling;
            }
        }

        /// <summary>
        /// Update enemy and create new
        /// </summary>
        /// <param name="mseconds">Milliseconds</param>
        public void Update()
        {
            float speed = this.speed;

            //calculate modifiers
            if (modifiers[ENEMYMOD.FREEZE].times != 0)
            {
                modifiers[ENEMYMOD.FREEZE].time--;
                speed /= 2f * (float)modifiers[ENEMYMOD.FREEZE].times;

                if (modifiers[ENEMYMOD.FREEZE].time == 0)
                    modifiers[ENEMYMOD.FREEZE].times = 0;
            }

            if (modifiers[ENEMYMOD.POISON].times != 0)
            {
                modifiers[ENEMYMOD.POISON].time--;
                if (modifiers[ENEMYMOD.POISON].time % 15 == 0)
                    ReduceHP(5 * (int)modifiers[ENEMYMOD.POISON].times);

                if (modifiers[ENEMYMOD.POISON].time == 0)
                    modifiers[ENEMYMOD.POISON].times = 0;
            }

            // get actual position
            internalTime += addTime * speed;
            if (internalTime >= 1.0f)
            {
                IsAlive = false;
                GamePage.PlayerHP -= 1;
            }

            Position = path.GetPosition(internalTime);
        }

        /// <summary>
        /// Draw enemy with set values
        /// </summary>
        public void Draw()
        {
            if (this.IsAlive)
            {
                Matrix[] Transforms = new Matrix[Model.Bones.Count];
                Model.CopyAbsoluteBoneTransformsTo(Transforms);

                foreach (ModelMesh mesh in Model.Meshes)
                {
                    foreach (BasicEffect currentEffect in mesh.Effects)
                    {
                        currentEffect.Projection = Camera.ProjectionMatrix;
                        currentEffect.View = Camera.ViewMatrix;
                        currentEffect.World = Matrix.CreateScale(this.Scale) * Matrix.CreateTranslation(this.Position);
                        currentEffect.EnableDefaultLighting();

                        foreach (EffectPass pass in currentEffect.CurrentTechnique.Passes)
                        {
                            pass.Apply();
                            mesh.Draw();
                        }
                    }
                }

                BoundingSphere boundingSpehere = BoundingSphere_Tools.TransformBoundingSphere(Model.Meshes[0].BoundingSphere, this.Position, this.Scale);

                Vector3 center = GamePage.Device.Viewport.Project(boundingSpehere.Center, Camera.ProjectionMatrix, Camera.ViewMatrix, Matrix.Identity);

                int healthBarLength = (int)(((2000 / MaxHp) * hp) / Camera.camPos.Z);
                int healthBarHeight = (int)(300 / Camera.camPos.Z);

                center.X -= (healthBarLength / 2);
                center.Y -= (1000 / Camera.camPos.Z);

                mBatch.Begin();
                if (hp > MaxHp/2)
                {
                    mBatch.Draw(mHealthBar, new Microsoft.Xna.Framework.Rectangle((int)center.X, (int)center.Y, healthBarLength, healthBarHeight), Microsoft.Xna.Framework.Color.Green);
                }
                else if (hp > MaxHp / 4)
                {
                    mBatch.Draw(mHealthBar, new Microsoft.Xna.Framework.Rectangle((int)center.X, (int)center.Y, healthBarLength, healthBarHeight), Microsoft.Xna.Framework.Color.Yellow);
                }
                else
                {
                    mBatch.Draw(mHealthBar, new Microsoft.Xna.Framework.Rectangle((int)center.X, (int)center.Y, healthBarLength, healthBarHeight), Microsoft.Xna.Framework.Color.Red);
                }

                for(int i = 0; i < hitDamages.Count; i++)
                {
                    if (hitDamages[i] > 0)
                    {
                        if (hitStartTimes.Count <= i)
                        {
                            hitStartTimes.Add(internalTime);
                        }
                        else
                        {
                            if (internalTime < (hitStartTimes[i] + 0.05f))
                            {
                                mBatch.DrawString(font1, String.Format("-{0}", hitDamages[i]), new Vector2(center.X, (center.Y - ((internalTime - hitStartTimes[i]) * 5000))), Microsoft.Xna.Framework.Color.White);
                            }
                            else
                            {
                                hitStartTimes.RemoveAt(i);
                                hitDamages.RemoveAt(i);
                            }
                        }
                    }
                }
                mBatch.End();
            }
        }

        public void Hit(Bullet bullet)
        {
            int damage = bullet.Damage - this.armor;
            if (damage > 0)
            {
                this.ReduceHP(damage);                
            }

            switch (bullet.BulletSpecial)
            {
                case TOWERSPECIAL.ice:
                    AddModifier(ENEMYMOD.FREEZE, 90);
                    break;

                case TOWERSPECIAL.poison:
                    AddModifier(ENEMYMOD.POISON, 90);
                    break;
            }
        }

        /// <summary>
        /// Add modifier for enemy
        /// </summary>
        /// <param name="mod">Type od modifier</param>
        /// <param name="time">How long modifier will be active - for death it is 0</param>
        public void AddModifier(ENEMYMOD mod, uint time)
        {
            modifiers[mod].time = time;
            modifiers[mod].times++;
        }

        /// <summary>
        /// Remove modifier for enemy
        /// </summary>
        /// <param name="mod">Type of modifier to remove</param>
        public void RemoveModifier(ENEMYMOD mod)
        {
            modifiers[mod].ClearTimes();
        }
    }
}
